# -*- coding: utf-8 -*-
from gluon.sqlhtml import *

def my_string_widget(field, value):
    return INPUT(_name=field.name,
                 _id="%s_%s" %(field._tablename, field.name),
                 _class=field.type,
                 _value=bool(value)*str(value)+" hello :)",
                 requires=field.requires)

def flatten(lst):  # http://www.daniweb.com/code/snippet216879.html
    for elem in lst:
        if type(elem) in (tuple, list):
            for i in flatten(elem):
                yield i
        else:
            yield elem
            
def upper_first(str):
    if not str: return str
    return str[0].upper()+str[1:]

## map program names to file names on disk
#~ letters_lt    = u'ąčęėįšųūžĄČĘĖĮŠŲŪŽ'
letters_lt    = 'ąčęėįšųūžĄČĘĖĮŠŲŪŽ'
letters_latin = 'aceeisuuzACEEISUUZ'
letters_lt_encutf    = u'ąčęėįšųūžĄČĘĖĮŠŲŪŽ'.encode('utf-8')
letters_latin_encutf = 'aceeisuuzACEEISUUZ'.encode('utf-8')

def latinize(str):
    for i in range(len(letters_latin)):
        ##print (letters_lt[i], letters_latin[i])
        str = str.replace(letters_lt[i], letters_latin[i])
    str = re.sub('[^\w ()-_]+', '', str)
    str = re.sub('\s+', '-', str)
    str = str.strip('- _')
##    return str.encode('ascii', 'ignore').strip('- _')
    return str


def extract_field_as_dict(data, fieldname, keyfield='id'):
    return dict([(row[keyfield], row[fieldname]) for row in data])

def prefetch_db_table_to_selected_rows(tablename):
    return db(db[tablename].id>0).select()

def prefetch_db_table_to_dict(tablename):
    return dict( [(r.id, r)   for r in prefetch_db_table_to_selected_rows(tablename)] )

# make aliases in names replace part "to" --> "as"
prefetch_db_table_as_selected_rows = prefetch_db_table_to_selected_rows
prefetch_db_table_as_dict = prefetch_db_table_to_selected_rows

# seems this doesn't work here :/ -- maybe because session is created later or what...??
cache_invalidate = session.cache_invalidate =  session.get('cache_invalidate', Storage())   # a workaround to reset caching (esp for GAE)

def cache_key_4example(id=None, content_type='example_view_4url', locale=None, force_get_key=False, data__Examples=None, data__Topics=None): # it is LOADED from function
    # a non reliable way to optimize a bit -- bugprone..
    if not force_get_key:
        if  not request.function.startswith('example_'):
            return "NO-KEY-NEEDED-HERE: " + request.env.path_info
    
    id = id or request.args(0)
    if data__Examples==None:
        #~ data__Examples=prefetch_db_table_to_dict('Examples')
        data__Examples=db.Examples # for one record there is no need to prefecha all table (if it isn't singleton)
    if data__Topics==None:
        data__Topics=db.Topics # for one record there is no need to prefecha all table (if it isn't singleton)
        
    example = data__Examples[id]
    locale = locale or user_prefs_get('locale')
    content_type = content_type  or request.function
    key = '--'.join( (
                    content_type, str(id), 
                    # those two are needed to  be able to clear cache when edited parent example --
                    # though this is heuristic metod to clear cache -- better would be to know exactly which examples depend on which
                    # to have tpl_influences table/tree + invalidation_timestamp (for each code_part?)

                    'topic', data__Topics[example.topic_id].name if example else '', # topic name
                    #~ topic_path_sep + str(db.Examples[request.args(0)].topic_id.topic_path) + topic_path_sep if db.Examples[request.args(0)] else '', # topic path surrounded by separators, to be universal for regexp "> Blah >"
                    'language',  example.lang_id.name if example else '',

                    'locale', locale,
                    #~ 'auth', str(auth.has_permission('update', 'Topics')),

                    #~ 'url', request.env.path_info,

               ) )
    #~ cache_track_key(key)
    #~ return key
    return latinize(key)


  
def cache_invalidate_4example(id, force_get_key=False, ):
    #~ for locale in data__Locales.values():
        #~ key = cache_key_4example(id=id, locale=locale.locale)
        key = cache_key_4example(id=id, force_get_key=force_get_key) # lets leave locale to be as in user_prefs
        #~ response.flash = ("cache_invalidate_4example %s: " % id ) + key
        session.cache_invalidate[key ]=True
        
def cache_invalidate_mark_done(key):
    #~ del cache_invalidate[key]
    session.cache_invalidate[key] = False
    cache_track_key(key)
    

def caching_time_get(key):
    """
    function to handle caching resets
    when the reset is on,
    it returns 0 once -- so the cache should expire
    and then it goes on standart expiries
    """
    if key in session.cache_invalidate and session.cache_invalidate[key]:        
    #~ if key in cache_invalidate and cache_invalidate[key]:        
    #~ if cache_invalidate[key]:        
        return 0
    else:
        return caching_time


#~ def cache_invalidate_all():
    #~ for item in db().select(db.Cache_keys.ALL):  #batch instead of looping might be better idea for GAE
        #~ cache_invalidate[item.key_] =  True

#~ @auth.requires_permission('update', 'Examples')
#~ def cache_update_crawler(clear=False, time_expire=None, regex=''): # deprecated in favour of cache_reset_all
    #~ if time_expire==None:
        #~ time_expire = caching_time
    #~ if clear:     # in nonGAE could just use cache.ram.clear( regex ) http://www.web2py.com/book/default/chapter/04#cache
        #~ time_expire = 0
        #~ ##~ r = re.compile(regex)
        #~ for item in db(db.Cache_keys.id > 0).select():  #batch instead of looping might be better idea for GAE
            #~ ##~ if r.match(key):
                #~ cache.ram(item.key_, lambda: '', time_expire=time_expire)
                #~ ##~ item.delete()                    
        #~ db(db.Cache_keys.id > 0).delete() # delete the keys after nulling time_expire -- but not regex sensitive here
        #~ import time
        #~ time.sleep(0.1) # just in case wait a bit -- so timeout would really happen
        #~ 
    #~ else:
        #~ pass
        #~ # MAIN PROBLEM: HOWTO imitate auth as nonadmin???
        
        # just call functions, which are decorated with cache
        # hm, but be carefull, as the functions are based on URL
        #~ results = {}
        #~ for locale in set([item for item in db().select(db.Locales.ALL)]):
            #~ results[locale]={}

            # update examples tree
            #~ results[locale]['topics_tree'] = LOAD( URL(f='default/topic_examples/1758/view',

            # now update all Examples -- batch or example id's prefetch would be better for GAE.. 
            #~ for item in db(db.Topics.id>0).select(id):
                #~ LOAD( f='topic_examples', args=[item.id, 'view'] )
                


def component_id(factory_name, tablename, record_id):
    return "%s_%s_%s" % (factory_name, tablename, record_id)


def escape4dom(msg):
	"""
	shoud convert any str to alphanumeric with underscores or dashes
	"""
	for a in " :."  :
		msg = msg.replace(a, '_')
	return msg

def mylog(*args, **kwargs):
    save_logs_in_session = False
    save_ERROR_logs_in_session = True
    if save_ERROR_logs_in_session and 'error' in str(args).lower():
        save_logs_in_session = True
    #~ session.logs = {}
    type = kwargs.get('type', '').upper()
    if save_logs_in_session:
        #~# if not 'logs' in response: response.logs = []    
        #~# response.logs.append( [args, kwargs ] )
        if not 'logs' in session: session.logs = {}

        if not 'id' in response:
            #~ #response.logs_id = request.env.path_info + str(request.now)
            response.id = request.env.path_info[len(request.application)+len(request.controller)+3:] +' - '+ str(request.now)
            session.logs[response.id] = response.logs = []

        for key, val in kwargs.items(): kwargs[key] = str(val) # escape to string, as Select objects can't be passed without rep
        args = map(str, args) 
        session.logs[response.id].insert(0, [type, args, kwargs ] )

    # to console
    if not request.env.web2py_runtime_gae: 
        print( [type, args, kwargs ] )

## maybe user_prefs could be inited here
# and used without session prepended
caching_time = 60*60*24 # in seconds = 24 hours

session.user_prefs =  session.get('user_prefs', Storage())  # init user_prefs variable,
                                                                    # seems better to use only session.user_prefs in noncontroller files

"""
for GAE this init needs to be repeated in every controller
"""
url_user_prefs = URL(r=request, c='default', f='ajax_update_user_prefs')

def LAZY_LOAD_triggering_inject(
        trigger_id,
        target_id,
        event='click',
        js_condition="true",  # probably this is not very necessary
    ):
    if event.startswith('on'): event = event[2:] # some evil AI :) # make 'onclick' to 'click'
    return SCRIPT("""
            $(document).ready(function() {
                        //$('#'+$.escape('%s'))  // helps with ids like 'C++'
                        // trigger_selector = '#'+$.escape('%(trigger_id)s');
                        // target_selector ='#'+$.escape('%(target_id)s');
                        // trigger_selector = '#%(trigger_id)s';
                        // target_selector ='#%(target_id)s';

                        //$(trigger_selector).%(event)s( function(e){  # namespaces for mixes :(, seems better to hardcode or make extra function

                        $('#'+$.escape('%(trigger_id)s')).%(event)s( function(e){
                        //$('#%(trigger_id)s').%(event)s( function(e){
                            //alert ($(this).attr('id') )    ;
                            if( %(js_condition)s ){
                                
                                //$(target_selector).each( function(x){ # same namespace problem as for trigger_selector
                                $('#'+$.escape('%(target_id)s')).each( function(x){
                                    alert ($(this).attr('id') + $(this).text() )
                                    
                                    if ($(this).text()=='') { // if nothing loaded still
                                         //web2py_component(action,  target);
                                         //alert($(this).data('action'));
                                         web2py_component( $(this).data('action') ,  $(this).attr('id') ); // load it
                                    }
                                } );
                                
                            }
                        } );
            });
            """ % locals() )
    


#~ def LAZY_LOAD_checkbox(trigger_id, target_id, var_name, checked=False, ajax_update_url=url_user_prefs):
#~ #not tested
    #~ return LABEL(
                #~ INPUT(_type='checkbox',_id=trigger_id,
                      #~ _onclick= """
                                    #~ target_selector = '#'+$.escape('%(target_id)s');
#~ 
                                    #~ url='%(ajax_update_url)s/%(var_name)s/'+this.checked;
                                    #~ ajax(url, [], "target_selector");
#~ 
                                    #~ if (this.checked) $(target_selector).show() ;
                                    #~ else $(target_selector).hide() 
                                    #~ 
                      #~ """ % locals(), 
                      #~ value = 'on' if checked else '',
                     #~ ),
               #~ #SPAN(_id=ajax_dbg_id) #"ajax_dbg_active_langs_"+lang.name)
            #~ )    

def LAZY_LOAD_directly_to_existing_target(target_id, url):
      return  SCRIPT_on_document_ready( "web2py_component('%(url)s', '%(target_id)s' );" % locals() )
              
    

def LAZY_LOAD_TARGET(target_id, url):
    return DIV(
                    DIV( '', _id = target_id) ,  # div must be empty at first
                    #~ SCRIPT(""" $(document).ready(function() { $($.escape('#%s')).data('action', '%s') });""" % -- this don't work
                    SCRIPT(""" $(document).ready(function() { $('#'+$.escape('%s')).data('action', '%s') });""" %
                                                                ( target_id, url) )
            )
            
def LOCALE_MENU():  # should be called in all controllers (or even maybe in layout??)
    #~ locale_form = SQLFORM.factory( # no luck..
        #~ Field('locale', default=user_prefs.locale ,  requires=IS_IN_DB( db, 'Locales.locale', 'Locales.name' )

    opts = []
    #~ opts.append(OPTION(T("Choose Language") ))
    for r in db(db.Locales.id>0).select():
        is_selected = dict(_selected='selected') if r.locale==session.user_prefs.locale else  {}
        opts.append(  OPTION(r.name, _value=r.locale, **is_selected ) )
        
    return SELECT(opts, _onchange="""
                        //ajax( '%%___________s'+'/locale/'+this.options[this.selectedIndex].value, [], '');
                        if ($(this).val() ){
                            ajax( '%s'+'/locale/'+$(this).val(), [], '');
                            setTimeout("location.reload();",500);  //location.reload();
                        }
                        """% url_user_prefs,
                  _id = "locale_menu",
                  _title = T("Choose Language")
                  
            )

    
def SCRIPT_on_document_ready(js_code):
    return SCRIPT(""" $(document).ready(function() { %s }); """ %js_code)
    
def user_prefs_set(var, value):
    """
    sets/remembers user preference 
    it could store it in db if user is logged_in
    or just in session for anonymous (and others as well)

    var -- variable/preference name
    value -- its value
    """
		
    session.user_prefs[var] = value
    user_prefs_store(var)  # store to db for logged in

# is probably not good way -- beter generate js code in html
#~ def ajax_eval_user_prefs_get():
    #~ var = request.args(0)
    #~ default_value = request.args(1) or ''
    #~ return user_prefs_get(var, default_value)



def user_prefs_init(var, default_value=None):
    if not var in session.user_prefs:  # save the default value as well
         user_prefs_set(var, default_value) # this would store it
         return True

def user_prefs_get(var, default_value=None):
    """
    gets user preference
    looks in session -- though in session it is much nicer session.user_prefs.var
    if not found in session, could look in db for logged in users
    """
    
    user_prefs_init(var, default_value) # inits if user_prefs.var is None

    #~ print var, user_prefs[var]     
    return session.user_prefs[var]

def user_prefs_store(var):
    """
    instructs to update to DB (for logged in users)
    """
    pass 

    
def my_hierarchical_options_widget(field, value, **attributes):
        
        requires = field.requires
        if not isinstance(requires, (list, tuple)):
            requires = [requires]
        if requires:
            if hasattr(requires[0], 'options'):
                options = requires[0].options()
            else:
                raise SyntaxError, 'widget cannot determine options of %s' \
                    % field
                  
        opts = []
        opts.append( OPTION('', _value=None) ) # for IS_EMPTY_OR part ... 
        
        #data=db(db.Topics.id>0).select() # !!! need filtering by version and author
        #names = extract_field_as_dict(data, 'name')
        #tree=topics_tree(data)
        try:
            names = dict( (int(k), v) for (k, v) in options if k)
            topics_paths = topics_paths_sorted(data=None)  # data is some select of db.Topics -- so could take only filtered by current_root_id in path
            mylog( topics_paths__sorted = topics_paths )
            for topic_path in topics_paths:
                level = len(topic_path)-1
                id = topic_path[-1]
                #~ msg_self = T("(as root)") if id==value else '' # value is supposed to be default value ;)
                opts.append( OPTION(' - '*level + names[id], _value=id) )
            #opts = [OPTION(v, _value=k) for (k, v) in options]
            

            default = dict(value=value,)
            attr = FormWidget._attributes(field, default, **attributes) # was OptionsWidget
            return SELECT(*opts, **attr)
        except Exception, e:
            mylog( "Error in my_hierarchical_options_widget(field=%s, value=%s, **attributes=%s)" % (field, value, attributes),
                    msg= str(e)
                  )
            raise e


class originalOptionsWidget(FormWidget):

    @staticmethod
    def has_options(field):
        """
        checks if the field has selectable options

        :param field: the field needing checking
        :returns: True if the field has options
        """

        return hasattr(field.requires, 'options')

    @staticmethod
    def widget(field, value, **attributes):
        """
        generates a SELECT tag, including OPTIONs (only 1 option allowed)

        see also: :meth:`FormWidget.widget`
        """

        default = dict(
            value=value,
            )
        attr = OptionsWidget._attributes(field, default, **attributes)

        requires = field.requires
        if not isinstance(requires, (list, tuple)):
            requires = [requires]
        if requires:
            if hasattr(requires[0], 'options'):
                options = requires[0].options()
            else:
                raise SyntaxError, 'widget cannot determine options of %s' \
                    % field
        opts = [OPTION(v, _value=k) for (k, v) in options]

        return SELECT(*opts, **attr)
